﻿using System;
using Google.Protobuf;

namespace GSE.Rpc
{
    public class Push : RpcObject
    {
        protected byte SeriesID;

        protected UInt16 SeqID;

        protected byte[] messageData;

        protected int messageCount;

        protected IMessage[] messages;

        public byte seriesID
        {
            get { return SeriesID; }
        }

        public UInt16 seqID
        {
            get { return SeqID; }
        }

        /// <summary>
        /// 编码消息数据区域,包括: 消息数量区 + (消息长度区 + 消息内容区) * n.
        /// </summary>
        /// <typeparam name="MsgType"></typeparam>
        /// <param name="messages"></param>
        /// <returns></returns>
        protected byte[] encodeMessageData<MsgType>(MsgType[] messages)
        {
            messageCount = messages.Length;
            var mbs = new byte[messages.Length][];
            var mLen = 0;

            for (var i = 0; i < messages.Length; i++)
            {
                mbs[i] = ((IMessage)messages[i]).ToByteArray();
                // 包含长度位 + 数据
                mLen += 2 + mbs[i].Length;
            }

            messageData = new byte[mLen];
            var offset = 0;
            var msgCountB = BitConverter.GetBytes((UInt16)messages.Length);
            Array.Reverse(msgCountB);
            Array.Copy(msgCountB, 0, messageData, offset, 2);
            offset += 2;

            for (var i = 0; i < mbs.Length; i++)
            {
                var lB = BitConverter.GetBytes((UInt16)mbs[i].Length);
                Array.Reverse(lB);
                Array.Copy(lB, 0, messageData, offset, lB.Length);
                offset += lB.Length;
                Array.Copy(mbs[i], 0, messageData, offset, mbs[i].Length);
                offset += mbs[i].Length;
            }
            return messageData;
        }

        /// <summary>
        /// 用于encode.
        /// </summary>
        /// <param name="seriesID"></param>
        /// <param name="seqID"></param>
        /// <param name="messages"></param>
        public Push(byte seriesID, UInt16 seqID, IMessage[] messages)
        {
            this.SeriesID = seriesID;
            this.SeqID = seqID;
            this.messages = messages;
        }

        /// <summary>
        /// 用于decode.
        /// </summary>
        /// <param name="messageType"></param>
        public Push()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rawData">包含消息长度数据区的消息数据.</param>
        public Push(byte[] rawData)
        {
            this.dataBytes = rawData;
        }

        /// <summary>
        /// 在调用Push(byte[] rawData)方法初始化后才能使用.
        /// </summary>
        public DT[] DecodeMessages<DT>()
        {
            return this.DecodeMessages<DT>(this.dataBytes);
        }

        /// <summary>
        /// 通过new Push&lt;T&gt; 初始化的实例才能调用此方法.
        /// </summary>
        /// <param name="data">包含消息长度数据区的消息数据</param>
        /// <returns></returns>
        /*
        public T[] DecodeMessages(byte[] data)
        {
            return this.DecodeMessages<T>(data);
        }*/

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data">不包含头部的数据.</param>
        public DT[] DecodeMessages<DT>(byte[] data)
        {
            var offset = 0;
            SeriesID = data[offset++];
            var seqIdB = new byte[2];
            Array.Copy(data, 1, seqIdB, 0, 2);
            Array.Reverse(seqIdB);
            SeqID = BitConverter.ToUInt16(seqIdB, 0);
            offset = 3;
            var msgCountB = new byte[2];
            Array.Copy(data, offset, msgCountB, 0, 2);
            Array.Reverse(msgCountB);
            var msgCount = BitConverter.ToUInt16(msgCountB, 0);
            offset += 2;

            var messages = new DT[msgCount];
            var index = 0;
            while (offset + 2 < data.Length - 1)
            {
                var mbLen = new byte[2];
                Array.Copy(data, offset, mbLen, 0, mbLen.Length);
                Array.Reverse(mbLen);
                var mLen = BitConverter.ToUInt16(mbLen, 0);
                offset += 2;
                if (offset + mLen > data.Length)
                {
                    throw new InvalidPushEventDataException($"Insufficient event data to be read. expects {mLen}, but remains {data.Length - offset}");
                }

                var m = (DT)Activator.CreateInstance(typeof(DT));

                try
                {
                    ((IMessage)m).MergeFrom(data, offset, mLen);

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }

                offset += mLen;
                messages[index++] = m;

            }
            return messages;
        }

        /// <summary>
        /// 组装完整的通讯包(包含包头).
        /// </summary>
        /// <returns></returns>
        public override byte[] Encode()
        {
            var mbs = new byte[messages.Length][];
            var mLen = 0;
            for (var i = 0; i < messages.Length; i++)
            {
                mbs[i] = messages[i].ToByteArray();
                // 包含长度位 + 数据
                mLen += 2 + mbs[i].Length;
            }
            // verNo + packet length + SeriesID + SeqID +  message data
            var data = new byte[1 + 4 + 1 + 2 + 2 + mLen];
            var offset = 0;
            data[offset] = (protoVerNo << 3) + (byte)GSE.LogicPacketType.EventPush;
            offset += 1;

            // -5, 去除固定头部的verNo和包长度.
            var dataLengthB = BitConverter.GetBytes((UInt32)(data.Length - 5));
            Array.Reverse(dataLengthB);
            Array.Copy(dataLengthB, 0, data, offset, 4);
            offset += 4;
            data[offset] = SeriesID;
            offset++;
            var seqIdB = BitConverter.GetBytes(SeqID);
            Array.Reverse(seqIdB);
            Array.Copy(seqIdB, 0, data, offset, 2);
            offset += 2;
            var msgCountB = BitConverter.GetBytes((UInt16)messages.Length);
            Array.Reverse(msgCountB);
            Array.Copy(msgCountB, 0, data, offset, 2);
            offset += 2;
            // 参数内容段.
            for (var i = 0; i < mbs.Length; i++)
            {
                var lB = BitConverter.GetBytes((UInt16)mbs[i].Length);
                Array.Reverse(lB);
                Array.Copy(lB, 0, data, offset, lB.Length);
                offset += lB.Length;
                Array.Copy(mbs[i], 0, data, offset, mbs[i].Length);
                offset += mbs[i].Length;
            }
            return data;
        }
    }
}
